#include "master.h"
#include <shlwapi.h>
#include <shlobj.h>
#include <commctrl.h>
#include <msxml2.h>
#include <objbase.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "playlist.h"
#include "misc.h"
#include "stream.h"
#include "gui.h"
#include "metaparse.h"
#include "comfunct/comfunct.h"

DWORD WINAPI OpenPlaylistFile(LPVOID lpThreadParams)
{
     OPENFILENAME ofn = {sizeof(OPENFILENAME), 0};
     TCHAR szMyMusicDir[MAX_PATH] = TEXT("");
	 HWND hwnd = GetAPWindow();
	 PLAYLISTDATA* pld = (PLAYLISTDATA*)lpThreadParams;

	 GetMyMusicDir(hwnd, szMyMusicDir);
     
     ofn.hwndOwner = hwnd;
	 ofn.lpstrFilter = GetLocalText(152);
	 ofn.lpstrFile = pld->szPlaylistPath;
	 ofn.nFilterIndex = 4;
	 ofn.lpstrTitle = GetLocalText(46);
	 ofn.nMaxFile = MAX_PATH;
	 ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_NONETWORKBUTTON | OFN_HIDEREADONLY;
	 ofn.lpstrDefExt = TEXT("m3u");
	 ofn.lpstrInitialDir = szMyMusicDir;
	 
	 if(GetOpenFileName(&ofn))
	 {
		 if(InvokePlaylist(pld) == 1)
         {
             ErrorMessage(GetLocalText(47));
			 SetStatus(hwnd, IDI_STATUS, 0, GetLocalText(7));
             return 1;
         }
     }
     else if(CommDlgExtendedError() == 0)
	 {
         SetStatus(hwnd, IDI_STATUS, 0, GetLocalText(7)); 
		 return 2;
     }
	 return 0;
}

DWORD WINAPI InvokePlaylist(LPVOID lpParams)
{
    PLAYLISTDATA* pld = (PLAYLISTDATA*)lpParams;
	int PlaylistType = GetPlaylistType(pld->szPlaylistPath);

	CoInitialize(NULL);
	
    if(PlaylistType == PLAYLIST_ERROR)
    {
        return 1;
    }
    else
    {
		switch(PlaylistType)
        {
			case PLAYLIST_ASX:
            {
                LPTSTR szTempPlaylistName = ASXToXML(pld->szPlaylistPath);

                memcpy(pld->szPlaylistPath, szTempPlaylistName, (MAX_PATH * sizeof(TCHAR)));                  
                free(szTempPlaylistName);
                if(ASXLoad(pld) == false)
			    {
				    CoUninitialize();
                    return 1;
			    }
            }
            break;
            case PLAYLIST_M3U:
            {
                if(M3ULoad(pld) == false)
			    {
					CoUninitialize();
                    return 1;
                }
            }
            break;
            case PLAYLIST_WPL:
            {
                if(WPLLoad(pld) == false)
				{
					CoUninitialize();
                    return 1;
				}
            }
            break;
        }		
        StartPlaylist(pld, GetAPWindow());
        return 0;
    }
}

int GetPlaylistType(LPTSTR szFileName)
{
     BYTE FileData[2] = {0}; /* We only need the first two chars to differentiate between file types */
     HANDLE hFile = NULL;
     DWORD dwBytesRead = 0;
     
     hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
     if(hFile == NULL || hFile == INVALID_HANDLE_VALUE)
     {
         return PLAYLIST_ERROR;
     }
     
     ReadFile(hFile, &FileData[0], 2, &dwBytesRead, NULL);
     CloseHandle(hFile);
     if(FileData[0] == TEXT('#'))
     {
         return PLAYLIST_M3U;
     }
     else if(FileData[0] == TEXT('<'))
     {
         if(tolower(FileData[1]) == TEXT('a'))
         {
              return PLAYLIST_ASX;
         }
         else if (FileData[1] == TEXT('?'))
         {
              return PLAYLIST_WPL;
         }
		 return PLAYLIST_ERROR;
     }
     else if(_tcsicmp(PathFindExtension(szFileName), TEXT("m3u")) == 0)
	 {
		 return PLAYLIST_M3U;
	 }
	 else return PLAYLIST_ERROR;
}

bool M3ULoad(PLAYLISTDATA* pld)
{
     /* File ref's are on every non-blank line not starting with a # */
	 char szThisFileLine[1000] = "";
	 FILE* M3UFile = NULL;
	 unsigned long lFilesInPlaylist = 0, lLoopCount = 0;
	 HWND hPlaylistItems = GetDlgItem(GetAPWindow(), IDI_PLAYLIST_VIEW);
	 DWORD dwThreadId = 0;
	 
	 M3UFile = _tfopen(pld->szPlaylistPath, TEXT("r"));
	 if(M3UFile == NULL)
	 {
		 ErrorMessage(GetLocalText(48));
		 return false;
	 }
	 
	 while(fgets(szThisFileLine, 1000, M3UFile))
	 {
		 if(szThisFileLine[0] == '#' || szThisFileLine[0] == '\r' || szThisFileLine[0] == '\n'
			|| szThisFileLine[0] == '.') /* we don't need comment or blank lines or relative paths */
		 {
			 continue;
		 }
		 ++lFilesInPlaylist; /* First we need to know how many files the playlist contains references to */
	 }
	 rewind(M3UFile);

	 pld->PlaylistItems = malloc(lFilesInPlaylist * sizeof(TCHAR*));
	 pld->lNumOfFilesinPlaylist = lFilesInPlaylist;

	 for(; lLoopCount < lFilesInPlaylist; ++lLoopCount)
	 {
		 while(fgets(szThisFileLine, 1000, M3UFile))
		 {
			  WCHAR wszWideFileLine[1000] = L"";
			  size_t s_tLineLength = strlen(szThisFileLine);
			  if(szThisFileLine[0] == '#' || szThisFileLine[0] == '\r') /* again we don't need comment or blank lines */
			  {
				  continue;
			  }

			  szThisFileLine[(s_tLineLength - 1)] = '\0';

			  if(PathIsURLA(szThisFileLine) == FALSE && PathFileExistsA(szThisFileLine) == FALSE)
			  {
				  continue;
			  }

			  pld->PlaylistItems[lLoopCount] = malloc(s_tLineLength * sizeof(TCHAR));

#ifdef UNICODE

			  MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szThisFileLine, -1, wszWideFileLine, (int)(s_tLineLength * 2));
			  /*_snwprintf(pld->PlaylistItems[lLoopCount], (int)s_tLineLength, L"%s", wszWideFileLine);*/
			  wcscpy(pld->PlaylistItems[lLoopCount], wszWideFileLine);

#else

			  /*_snprintf(pld->PlaylistItems[lLoopCount], s_tLineLength, "%s", szThisFileLine);*/
			  strcpy(pld->PlaylistItems[lLoopCount], szThisFileLine);

#endif
			  break; /* So we go back to the for loop and increment lLoopCount, otherwise we keep overwriting the first element */

		  } /* end of while loop */		 

	 } /* end of for loop */
	 fclose(M3UFile);
	 CloseHandle(CreateThread(NULL, 0, BuildPlaylistView, (LPVOID)pld, 0, &dwThreadId));
     return true;
}

bool WPLLoad(PLAYLISTDATA* pld)
{
     /* File ref's are in field body->seq->media, attribute src */
     IXMLDOMDocument2 *pPlaylist = NULL;
     IXMLDOMNodeList *pNodes = NULL;
     long lNoOfItems = 0;
     BSTR bstrNodeName;
     
     pPlaylist = LoadXML(pld->szPlaylistPath);
     if(pPlaylist == NULL)
     {
          return false;
     }
     bstrNodeName = SysAllocString(L"media");
     pPlaylist->lpVtbl->getElementsByTagName(pPlaylist, bstrNodeName, &pNodes);
     pNodes->lpVtbl->get_length(pNodes, &lNoOfItems);
	 SysFreeString(bstrNodeName);

	 XMLToPlaylist(pld, L"src", pNodes, lNoOfItems);

     SAFE_RELEASE(pPlaylist);
     SAFE_RELEASE(pNodes);
     return true;
}
/*
bool ASXLoad(PLAYLISTDATA* pld)
{
     /* File ref's are in field entry->SourceUrl 
     IXMLDOMDocument2 *pPlaylist = NULL;
     IXMLDOMNodeList *pNodes = NULL;
     IXMLDOMNamedNodeMap *pAttributes = NULL;
	 IXMLDOMNode *pThisEntry = NULL, *pAttributeProperty = NULL;
	 BSTR bstrNodeName, bstrAttributeName = SysAllocString(L"Name"), bstr;
     long lNoOfItems = 0, loop = 0, lFileCount = 0;
     DWORD dwStartTime = 0, dwEndTime = 0, dwResult = 0;
	 char szBuf[200] = "";
     
     pPlaylist = LoadXML(pld->szPlaylistPath);
     if(pPlaylist == NULL)
     {
          return false;
     }
     bstrNodeName = SysAllocString(L"Param");
     pPlaylist->lpVtbl->getElementsByTagName(pPlaylist, bstrNodeName, &pNodes);
     pNodes->lpVtbl->get_length(pNodes, &lNoOfItems);
     timeBeginPeriod(1);
     dwStartTime = timeGetTime();
     for(; loop < lNoOfItems; loop++)
     {
         pNodes->lpVtbl->get_item(pNodes, loop, &pThisEntry);
         pThisEntry->lpVtbl->get_attributes(pThisEntry, &pAttributes);
         pAttributes->lpVtbl->getNamedItem(pAttributes, bstrAttributeName, &pAttributeProperty);
         pAttributeProperty->lpVtbl->get_text(pAttributeProperty, &bstr);
         if(wcscmp(bstr, L"SourceURL") == 0)
         {
             ++lFileCount;
         }
         SAFE_RELEASE(pAttributeProperty);
         SAFE_RELEASE(pAttributes);
         SAFE_RELEASE(pThisEntry);
         SysFreeString(bstr);
     }
     dwEndTime = timeGetTime();
	 dwResult = dwEndTime - dwStartTime;
	 sprintf(szBuf, "The calculation took %d milliseconds", dwResult);
	 ErrorMessageA(szBuf);
     timeEndPeriod(1);
     SysFreeString(bstrAttributeName);
	 SysFreeString(bstrNodeName);

	 XMLToPlaylist(pld, L"href", pNodes, lNoOfItems);
     
     SAFE_RELEASE(pNodes);
	 SAFE_RELEASE(pPlaylist);
     return true;
}*/

bool ASXLoad(PLAYLISTDATA* pld)
{
     /* File ref's are in field entry->SourceUrl */
     IXMLDOMDocument2 *pPlaylist = NULL;
     IXMLDOMNodeList *pNodes = NULL;
	 BSTR bstrNodeName;
     long lNoOfItems = 0;
     
     pPlaylist = LoadXML(pld->szPlaylistPath);
     if(pPlaylist == NULL)
     {
          return false;
     }
     bstrNodeName = SysAllocString(L"Ref");
     pPlaylist->lpVtbl->getElementsByTagName(pPlaylist, bstrNodeName, &pNodes);
     pNodes->lpVtbl->get_length(pNodes, &lNoOfItems);
	 SysFreeString(bstrNodeName);

	 XMLToPlaylist(pld, L"href", pNodes, lNoOfItems);
     
     SAFE_RELEASE(pNodes);
	 SAFE_RELEASE(pPlaylist);
     return true;
}

void XMLToPlaylist(PLAYLISTDATA* pld, LPWSTR szAttribute, IXMLDOMNodeList* pParentNodes, unsigned long lNoOfItems)
{
	IXMLDOMNamedNodeMap *pAttributes = NULL;
	IXMLDOMNode *pThisEntry = NULL, *pAttributeProperty = NULL;
	unsigned long lLoopCounter = 0L;
	DWORD dwThreadId = 0;
	HWND hPlaylistItems = GetDlgItem(GetAPWindow(), IDI_PLAYLIST_VIEW);

	pld->PlaylistItems = malloc(lNoOfItems * sizeof(TCHAR*));

	for(; lLoopCounter < lNoOfItems; lLoopCounter++)
	{
		 BSTR bstr;
		 char* szNarrowText = NULL;
		 size_t s_tStrlen = 0;

		 pParentNodes->lpVtbl->get_item(pParentNodes, lLoopCounter, &pThisEntry); /* Get the [loop]'th media node and put into pThisEntry */
		 pThisEntry->lpVtbl->get_attributes(pThisEntry, &pAttributes); /* Put its attributes into pAttributes */
		 bstr = SysAllocString(szAttribute);
		 pAttributes->lpVtbl->getNamedItem(pAttributes, bstr, &pAttributeProperty); /* Get the passed in attribute property and put its reference into pAttributeProperty*/
		 SysFreeString(bstr);
		 pAttributeProperty->lpVtbl->get_text(pAttributeProperty, &bstr); /* Put the text associated with attribute into the bstr variable */

		 s_tStrlen = (wcslen(bstr) + 1); /* wcslen returns the number of chars not bytes and BSTR's are always wide strings for us */
		 
#ifndef UNICODE

         szNarrowText = ASCIIConversion(bstr);
		 pld->PlaylistItems[lLoopCounter] = malloc(s_tStrlen);
		 _snprintf(pld->PlaylistItems[lLoopCounter], s_tStrlen, "%s", szNarrowText);
		 //strcpy(pld->PlaylistItems[lLoopCounter], szNarrowText);
         FreePointer(szNarrowText);
         
#else	 

		 pld->PlaylistItems[lLoopCounter] = malloc(s_tStrlen * sizeof(WCHAR));
		 _snwprintf(pld->PlaylistItems[lLoopCounter], s_tStrlen, L"%s", bstr);
		 //wcscpy(pld->PlaylistItems[lLoopCounter], bstr);
		 
#endif

		 SysFreeString(bstr);
		 SAFE_RELEASE(pAttributeProperty);
		 SAFE_RELEASE(pAttributes);
		 SAFE_RELEASE(pThisEntry);
	}
	pld->lNumOfFilesinPlaylist = lLoopCounter;
	CloseHandle(CreateThread(NULL, 0, BuildPlaylistView, (LPVOID)pld, 0, &dwThreadId));
	return;
}

IXMLDOMDocument2* LoadXML(LPTSTR szPlaylistPath)
{
     IXMLDOMDocument2 *pPlaylist = NULL;
     VARIANT_BOOL bSuccess = VARIANT_FALSE;
     VARIANT var;
     HRESULT hr = 0;
	 WCHAR* wideszPlaylistPath = NULL;
     
     hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument2, (void**)&pPlaylist);
     if(FAILED(hr))
     {
         HRErrorInfo(hr);
         return NULL;
     }
     
     VariantInit(&var);

#ifndef UNICODE

	 wideszPlaylistPath = UnicodeConversion(szPlaylistPath);
	 V_BSTR(&var) = SysAllocString(wideszPlaylistPath);
	 FreePointer(wideszPlaylistPath);

#else
     
     V_BSTR(&var) = SysAllocString(szPlaylistPath);

#endif

     V_VT(&var) = VT_BSTR;
     hr = pPlaylist->lpVtbl->load(pPlaylist, var, &bSuccess);
     
     if(bSuccess != VARIANT_TRUE || FAILED(hr))
     {
         SAFE_RELEASE(pPlaylist);
         HRErrorInfo(hr);
         VariantClear(&var);
         return NULL;
     }
     VariantClear(&var); // calls SysFreeString internally
     return pPlaylist;
}

LPTSTR ASXToXML(LPTSTR szOldFileName)
{
     HANDLE hOldFile = NULL, hAsxmlFile = NULL;
     BYTE* byFileData = NULL;
     BYTE* byCorrectedFileData = NULL;
     DWORD dwFileSize = 0, dwBytesRead = 0, dwBytesWritten = 0;
     DWORD loop = 0, dwCorrectedByteCount = 0;
     int iAmpTextCount = 0, iAmpCharSize = 0;
     const char byAmpersandText[] = "&amp;";
     TCHAR* szNewFileName = malloc((MAX_PATH * sizeof(TCHAR)));
     LPTSTR IsValidXML = NULL;
     
	 iAmpCharSize = sizeof(byAmpersandText) - 1; /* Don't want to count the NULL */

     memcpy(szNewFileName, szOldFileName, (MAX_PATH * sizeof(TCHAR)));
     IsValidXML = _tcsstr(PathFindExtension(szOldFileName), TEXT("ml"));
     
     if(IsValidXML != NULL)
     {
          return szNewFileName;
     }
     
     _tcscat(szNewFileName, TEXT("ml"));
     
     if(PathFileExists(szNewFileName) == TRUE)
     {
          return szNewFileName;
     }
     
     hOldFile = CreateFile(szOldFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
     hAsxmlFile = CreateFile(szNewFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
     
     dwFileSize = GetFileSize(hOldFile, NULL);
     
     byFileData = malloc(dwFileSize);
     byCorrectedFileData = malloc(dwFileSize * 2);
     
	 memset(byCorrectedFileData, 0, (dwFileSize * 2));
     
     ReadFile(hOldFile, &byFileData[0], dwFileSize, &dwBytesRead, NULL);
     CloseHandle(hOldFile);

     for(; loop < dwFileSize; ++loop) // This is definitely not the fastest way to do this, _tcsspn may be an example
     {         
         if(byFileData[loop] == '&') 
         {
			 for(iAmpTextCount = 0; iAmpTextCount < iAmpCharSize; iAmpTextCount++) 
             {
                  byCorrectedFileData[dwCorrectedByteCount] = byAmpersandText[iAmpTextCount];
                  ++dwCorrectedByteCount;
             }
         }
		 else if ((byFileData[loop] == '\r' || byFileData[loop] == '\n') || (byFileData[loop] >= ' ' && byFileData[loop] <= 'z'))
		 {
			 byCorrectedFileData[dwCorrectedByteCount] = byFileData[loop];
			 ++dwCorrectedByteCount;
		 }
     }

     free(byFileData);     
     WriteFile(hAsxmlFile, &byCorrectedFileData[0], dwCorrectedByteCount, &dwBytesWritten, NULL);
     free(byCorrectedFileData);
     CloseHandle(hAsxmlFile);
     
     return szNewFileName;
}

void TogglePlaylistMenu(HWND hwnd, bool bState)
{
	HMENU hParent = GetMenu(hwnd), hFileMenu = NULL;
	hFileMenu = GetSubMenu(hParent, FILE_MENU);
	if(bState == true)
	{
		EnableMenuItem(hParent, PLAYLIST_MENU, MF_BYPOSITION | MF_ENABLED);
	}
	else
	{
		EnableMenuItem(hParent, PLAYLIST_MENU, MF_BYPOSITION | MF_GRAYED);
	}
	DrawMenuBar(hwnd);
	return;
}

unsigned long GetRandomTrack(PLAYLISTDATA* pld)
{
	unsigned long lRandomTrack = 0L;
	srand((UINT)GetTickCount());
	if(pld->lNumOfFilesinPlaylist == 1L)
	{
		return 0L;
	}
	do
	{

#if !defined(_MSC_VER) || _MSC_VER < 1400 /* If we're compiling in something other than VS2005 we still have to use the old random number generator */

        lRandomTrack = (unsigned long)(pld->lNumOfFilesinPlaylist * rand()/(RAND_MAX + 1.0));

#elif defined(_MSC_VER) && _MSC_VER >= 1400 /* If you're compiling with VS2005 you can use the rand_s function for a better spread of random numbers */

		rand_s((UINT*)&lRandomTrack);
		lRandomTrack = lRandomTrack % pld->lNumOfFilesinPlaylist;

#endif

    }
    while (lRandomTrack == pld->lCurrentPlayingFile); /* Keep getting a random number until it's not the same as the number of the currently playing file */
    return lRandomTrack;
}

DWORD WINAPI RawDataToPlaylist(LPVOID lpParam)
{
	RAWDATA* rd = (RAWDATA*)lpParam;
	TCHAR szDirectory[MAX_PATH] = TEXT("");
	unsigned long lLoopCount = 0L, lNumFiles = rd->lNumFiles;
	size_t s_tDirectorySize = (_tcslen(rd->szPlaylist) + 1);
	TCHAR* szBufferPointer = NULL;
	TCHAR szIntermediateBuffer[MAX_PATH] = TEXT("");
	size_t s_tStrlen = 0;
	DWORD dwThreadId = 0;
	HWND hPlaylistItems = GetDlgItem(rd->hwnd, IDI_PLAYLIST_VIEW);

	_tcscpy(szDirectory, rd->szPlaylist);

	rd->pld->PlaylistItems = malloc(lNumFiles * sizeof(TCHAR*));

	_tcscat(szDirectory, TEXT("\\"));

    szBufferPointer = rd->szPlaylist + s_tDirectorySize;
	SendMessage(hPlaylistItems, LB_INITSTORAGE, (WPARAM)lNumFiles, (LPARAM)MAX_PATH);

	for(; lLoopCount < lNumFiles; lLoopCount++)
	{
		_tcscpy(szIntermediateBuffer, szDirectory);
		_tcscat(szIntermediateBuffer, szBufferPointer);
		s_tStrlen = (_tcslen(szIntermediateBuffer) + sizeof(TCHAR));
		rd->pld->PlaylistItems[lLoopCount] = malloc(s_tStrlen * sizeof(TCHAR));
		_tcscpy(rd->pld->PlaylistItems[lLoopCount], szIntermediateBuffer);
		szBufferPointer = _tcschr((szBufferPointer + sizeof(TCHAR)), TEXT('\0')) + 1; /* Set the pointer to the character after the next NULL */
	}
	rd->pld->lNumOfFilesinPlaylist = lNumFiles;
	CloseHandle(CreateThread(NULL, 0, BuildPlaylistView, (LPVOID)rd->pld, 0, &dwThreadId));
	StartPlaylist(rd->pld, rd->hwnd);
	return 0;
}

void SavePlaylist(HWND hwnd, PLAYLISTDATA* pld)
{
	OPENFILENAME ofn = {0};
	TCHAR szFileName[MAX_PATH] = TEXT("");
	
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = hwnd;
	ofn.lpstrFilter = GetLocalText(49);
	ofn.lpstrFile = szFileName;
	ofn.nFilterIndex = 1;
	ofn.lpstrTitle = GetLocalText(50);
	ofn.nMaxFile = MAX_PATH;
	ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_NONETWORKBUTTON | OFN_OVERWRITEPROMPT;
	ofn.lpstrDefExt = TEXT("m3u");

	if(GetSaveFileName(&ofn))
	{
		SavePlaylistAsM3U(pld, szFileName);
	}
	return;
}


void SavePlaylistAsM3U(PLAYLISTDATA* pld, LPCTSTR szFileName)
{
	HANDLE hFile = NULL;
	DWORD dwBytesWritten = 0;
/* Again uncomment this for internationalisation

	TCHAR* szFileHeader = GetLocalText(number);
	char* szNarrowHeader = NULL;

*/
	const char szFileHeader[] = "#EXTM3U Playlist written by AudioPlayer\r\n\r\n";
	const char szLineEndings[] = "\r\n";
	unsigned long lLoopCount = 0L;
	char* szConversion = NULL;
	size_t s_tBufferLength = 0;
	int iBufferLocation = 0;
	char* szBuffer = malloc((MAX_PATH * pld->lNumOfFilesinPlaylist) + sizeof(szFileHeader));

/* When we do the GetLocalText's uncomment this

	char* szBuffer = NULL;

#ifdef UNICODE

	szNarrowHeader = ASCIIConversion(szFileHeader);

#else

	szNarrowHeader = szFileHeader;

#endif 

	szBuffer = malloc((MAX_PATH * pld->lNumOfFilesinPlaylist) + strlen(szNarrowHeader) + 1);

	*/

	hFile = CreateFile(szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if(hFile == NULL || hFile == INVALID_HANDLE_VALUE)
	{
		ErrorInfo();
		ErrorMessage(GetLocalText(51));
		free(szBuffer);
		return;
	}

	/* again uncomment
	strcpy(szBuffer, szNarrowHeader);
	iBufferLocation = strlen(szNarrowHeader); /* Minus 1 because we don't want to count the terminating null */

	strcpy(szBuffer, szFileHeader);
	iBufferLocation = (sizeof(szFileHeader) - 1); /* Minus 1 because we don't want to count the terminating null */

	for(; lLoopCount < pld->lNumOfFilesinPlaylist; lLoopCount++)
	{

#ifdef UNICODE

		szConversion = ASCIIConversion(pld->PlaylistItems[lLoopCount]);
		s_tBufferLength = strlen(szConversion);
		strcat(szBuffer, szConversion);
		iBufferLocation += (int)s_tBufferLength;
		FreePointer(szConversion);
#else

		s_tBufferLength = strlen(pld->PlaylistItems[lLoopCount]);
		strcat(szBuffer, pld->PlaylistItems[lLoopCount]);
		iBufferLocation += (int)s_tBufferLength;

#endif

		strcat(szBuffer, szLineEndings);
		iBufferLocation += (sizeof(szLineEndings) - 1); /* Don't count the terminating NULL again */

	}

	WriteFile(hFile, szBuffer, iBufferLocation, &dwBytesWritten, NULL);
	free(szBuffer);
	CloseHandle(hFile);
/* When we do the GetLocalText's uncomment this

#ifdef UNICODE

	FreePointer(szNarrowHeader);

#endif */
	return;
}

void StartPlaylist(PLAYLISTDATA* pld, HWND hwnd)
{
	if(pld->bShuffle == true)
	{
		pld->lCurrentPlayingFile = GetRandomTrack(pld);
	}
	else pld->lCurrentPlayingFile = 0;
	TogglePlaylistMenu(hwnd, true);
	SetStatus(hwnd, IDI_STATUS, 0, GetLocalText(7));
	ProcessFile(hwnd, IsIconic(hwnd), pld->PlaylistItems[pld->lCurrentPlayingFile], pld);
	return;
}

void AddFileToPlaylistView(HWND hPlaylistItems, TCHAR* szFile, unsigned long lPos)
{
	IWMMetadataEditor* pWMEdit = NULL;
	WCHAR* wszTitle = NULL, *wszFile = NULL;
	LVITEM lvi[2] = {{0}, {0}};

	lvi[0].mask = LVIF_TEXT | LVIF_PARAM;
	lvi[0].iItem = lPos;
	lvi[0].iSubItem = 0;
	lvi[0].lParam = lPos;

#ifdef UNICODE

	pWMEdit = CreateReader(szFile);

#else
	
	wszFile = UnicodeConversion(szFile);
	pWMEdit = CreateReader(wszFile);
	
#endif

	wszTitle = GetAttribute(pWMEdit, g_wszWMTitle);
	if(wszTitle == NULL || (wcscmp(wszTitle, L"") == 0))
	{

#ifdef UNICODE

		WCHAR* wszFileTitle = PathFindFileName(szFile);
		PathRemoveExtension(wszFileTitle);
		lvi[0].pszText = wszFileTitle;

#else

		char* szFileTitle = PathFindFileName(szFile);
		PathRemoveExtension(szFileTitle);
		lvi[0].pszText = szFileTitle;

#endif
		
		ListView_InsertItem(hPlaylistItems, &lvi[0]);
	}
	else
	{

#ifdef UNICODE

		lvi[0].pszText = wszTitle;

#else
		lvi[0].pszText = ASCIIConversion(wszTitle);

#endif

		ListView_InsertItem(hPlaylistItems, &lvi[0]);
		free(wszTitle);

#ifndef UNICODE

		FreePointer(lvi[0].pszText);

#endif

	}

#ifndef UNICODE

	FreePointer(wszFile);

#endif

	pWMEdit->lpVtbl->Close(pWMEdit);
	SAFE_RELEASE(pWMEdit);
	return;
}

int CALLBACK CompareListItems(LPARAM lParamItem1, LPARAM lParamItem2, LPARAM lParamHWND)
{
	LVFINDINFO lvf[2] = {{LVFI_PARAM, 0}, {LVFI_PARAM, 0}};
	LVITEM lvi[2] = {{0}, {0}};
	TCHAR szText1[MAX_PATH] = TEXT(""), szText2[MAX_PATH] = TEXT("");

	lvi[0].pszText = szText1;
	lvi[0].mask = LVIF_TEXT;
	lvi[0].cchTextMax = MAX_PATH;
	lvi[1].pszText = szText2;
	lvi[1].mask = LVIF_TEXT;
	lvi[1].cchTextMax = MAX_PATH;

	lvf[0].lParam = lParamItem1;
	lvf[1].lParam = lParamItem2;
	lvi[0].iItem = ListView_FindItem((HWND)lParamHWND, -1, &lvf[0]);
	lvi[1].iItem = ListView_FindItem((HWND)lParamHWND, -1, &lvf[1]);

	ListView_GetItem((HWND)lParamHWND, &lvi[0]);
	ListView_GetItem((HWND)lParamHWND, &lvi[1]);

	return _tcsicmp(szText1, szText2);
}

DWORD WINAPI PlaylistSortSupportThread(LPVOID lpParam)
{
	HWND hPlaylistItems = (HWND)lpParam;
	ListView_SortItems((HWND)lpParam, CompareListItems, (LPARAM)lpParam);
	return 0;
}

DWORD WINAPI BuildPlaylistView(LPVOID lpParam)
{
	unsigned long lFile = 0;
	HWND hwnd = GetAPWindow();
	HWND hPlaylistItems = GetDlgItem(hwnd, IDI_PLAYLIST_VIEW);
	HWND hPlaylistText = GetDlgItem(hwnd, IDI_PLAYLIST_TITLE);
	MENUITEMINFO mii = {sizeof(MENUITEMINFO), 0};
	RECT rc = {0};
	HMENU hParentMenu = GetMenu(hwnd), hSubMenu = GetSubMenu(hParentMenu, PLAYLIST_MENU);
	PLAYLISTDATA* pld = (PLAYLISTDATA*)lpParam;
	TCHAR szPlaylistTitle[260] = TEXT(""), szTitleCopy[260] = TEXT("");

	_tcscpy(szPlaylistTitle, GetLocalText(6));

	if(_tcscmp(TEXT(""), pld->szPlaylistPath) != 0)
	{
		TCHAR szPlaylistFileName[100] = TEXT(" - ");
		_tcscat(szPlaylistFileName, PathFindFileName(pld->szPlaylistPath));
		_tcscat(szPlaylistTitle, szPlaylistFileName);
	}
	
	_tcscpy(szTitleCopy, szPlaylistTitle);
	_tcscat(szTitleCopy, GetLocalText(52));
	SetWindowText(hPlaylistText, szTitleCopy);

	ListView_SetItemCount(hPlaylistItems, pld->lNumOfFilesinPlaylist);
	for(; lFile < pld->lNumOfFilesinPlaylist; ++lFile)
	{
		/* AddFileToPlaylistView changes the second parameter so we make a copy of the file's path and send that in instead */
		TCHAR szFile[MAX_PATH] = TEXT("");
		_tcscpy(szFile, pld->PlaylistItems[lFile]);
		AddFileToPlaylistView(hPlaylistItems, szFile, lFile);
	}
	PlaylistSortSupportThread((LPVOID)hPlaylistItems);
	SetWindowText(hPlaylistText, szPlaylistTitle);
	mii.fMask = MIIM_STATE;
	GetMenuItemInfo(hSubMenu, IDI_PLAYLIST_SHOW, FALSE, &mii);
	GetWindowRect(hwnd, &rc);
	if((mii.fState == MFS_CHECKED) && ((rc.right - rc.left) < 400))
	{
		AdjustWindowWidth(hwnd, PLAYLIST_VIEW_WIDTH);
	}
	return 0;
}

void GetPVSelection(HWND hPlaylistItems, LVITEM* lplvi)
{
	lplvi->iItem = ListView_GetSelectionMark(hPlaylistItems);
	lplvi->iSubItem = 0;
	ListView_GetItem(hPlaylistItems, lplvi);
	return;
}

void FolderFilesToPlaylist(TCHAR* szPattern, PLAYLISTDATA* pld)
{
	WIN32_FIND_DATA wfd = {0};
	HANDLE hFile = FindFirstFile(szPattern, &wfd); /* fills wfd with the folder info, not the first file in the folder */
	unsigned long lLoopCount = 0;
	DWORD dwThreadId = 0;
	TCHAR szFolder[MAX_PATH] = TEXT("");
	size_t s_tPatLen = _tcslen(szPattern);

	memcpy(szFolder, szPattern, s_tPatLen * sizeof(TCHAR));
	szFolder[s_tPatLen - 3] = 0;

	while(FindNextFile(hFile, &wfd) != 0)
	{
		TCHAR szFullFileName[MAX_PATH] = TEXT("");
		if((wfd.cFileName[0] == TEXT('.')) || (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) /* Again, not interested in any relative directory paths */
		{
			continue;
		}
		_tcscpy(szFullFileName, szFolder);
		_tcscat(szFullFileName, wfd.cFileName);
		pld->PlaylistItems[lLoopCount] = malloc((_tcslen(szFullFileName) + sizeof(TCHAR)) * sizeof(TCHAR));
		_tcscpy(pld->PlaylistItems[lLoopCount], szFullFileName);
		++lLoopCount;
	}
	FindClose(hFile);

	StartPlaylist(pld, GetAPWindow());
	CloseHandle(CreateThread(NULL, 0, BuildPlaylistView, (LPVOID)pld, 0, &dwThreadId));
	return;
}
